package ac.rsync.pojo;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.dom4j.Document;
import org.dom4j.io.SAXReader;

import ac.java.utils.AcDownLoad;
import ac.java.utils.AcFile;
import ac.java.utils.AcFileMd5;
import ac.java.utils.AcHttp;
import ac.java.utils.Vaild;
import ac.java.utils.XmlDom4j;

public class AcHttpRsync {
	private Map serverFsMap = null;
	private Map localFsMap = null;
	private Map downFsMap = null;
	private String serverPath;
	private String localPath;
	private String serverUrl = null;

	public void setServerUrl(String url) {
		this.serverFsMap = null;
		this.serverUrl = url;
	}

	public void setServerPath(String path) {
		this.serverFsMap = null;
		this.serverPath = path;
	}

	public void setLocalPath(String path) {
		this.localFsMap = null;
		this.localPath = path;
	}

	public String getServerPath() {
		return this.serverPath;
	}

	public String getLocalPath() {
		return this.localPath;
	}

	public String getServerUrl() {
		return this.serverUrl;
	}

	public boolean rsync() {
		
		// 清理本地文件
		boolean cRs = this.clearLocalFs();

		if (!cRs) {
			return false;
		}

		// 同步文件
		boolean dRs = this.downLoadFs();
		if (!dRs) {
			return false;
		}

		return true;
	}

	protected Map getLocalFsMap() {
		if (!Vaild.isEmpty(this.localFsMap)) {
			return this.localFsMap;
		}

		this.localFsMap = AcFile.listMd5Files(this.getLocalPath());
		return this.localFsMap;
	}

	protected boolean clearLocalFs() {
		// 本地文件
		Map<String, Map> lFsMap = this.getFsMap(this.getLocalFsMap(), false);
		if (Vaild.isEmpty(lFsMap)) {
			return true;
		}

	//	System.out.println("lFsMap:" + lFsMap);

		// 源文件
		Map<String, Map> sFsMap = this.getFsMap(this.getServerFsMap(), true);
		if (Vaild.isEmpty(sFsMap)) {
			return false;
		}

		//System.out.println("sFsMap:" + sFsMap);

		// 较验目录

		// 删除多余目录
		if (lFsMap.containsKey("d")) {
			Map<String, String> lDMap = lFsMap.get("d");

			if (sFsMap.containsKey("d")) {
				Map<String, String> sDMap = sFsMap.get("d");
				Iterator lDIt = lDMap.entrySet().iterator();
				while (lDIt.hasNext()) {
					Entry entry = (Entry) lDIt.next();
					String dPath = (String) entry.getKey();

					if (!sDMap.containsKey(dPath)) {
						// System.out.println(this.getFsPath(dPath,false));
						String localPath = this.getLocalPath()	+ System.getProperty("file.separator") + dPath;
						 System.out.println(localPath);
						if (!AcFile.deleteDir(localPath)) {
							 System.out.println(localPath);
							return false;
						}
					}
				}
			} else {
				File dirFs = new File(this.getLocalPath());
				File dirFss[] = dirFs.listFiles();
				for (int i = 0; i < dirFss.length; i++) {
					if (dirFss[i].isDirectory()) {
						if (!AcFile.deleteDir(dirFss[i])) {
							return false;
						}
					}
				}
			}
		}

		// 删除多余文件
		if (lFsMap.containsKey("f")) {
			Map<String, String> lFMap = lFsMap.get("f");

			if (sFsMap.containsKey("f")) {
				Map<String, String> sFMap = sFsMap.get("f");
				Iterator lFIt = lFMap.entrySet().iterator();
				while (lFIt.hasNext()) {
					Entry entry = (Entry) lFIt.next();
					String fPath = (String) entry.getKey();

					if (!sFMap.containsKey(fPath)) {
						String localPath = this.getLocalPath()
								+ System.getProperty("file.separator") + fPath;
						// System.out.println(localPath);
						if (!AcFile.deleteFile(localPath)) {
							return false;
						}
					}
				}
			} else {
				Iterator lFIt = lFMap.entrySet().iterator();
				while (lFIt.hasNext()) {
					Entry entry = (Entry) lFIt.next();
					String fPath = (String) entry.getKey();
					String localPath = this.getLocalPath()
							+ System.getProperty("file.separator") + fPath;

					if (!AcFile.deleteFile(localPath)) {
						return false;
					}
				}
			}
		}

		return true;
	}

	protected boolean downLoadFs() {
		Map fsMap = this.getServerFsMap();
		if (Vaild.isEmpty(fsMap)) {
			return true;
		}

		// 创建目录结构
		if (fsMap.containsKey("d")) {
			Map dMap = (Map) fsMap.get("d");

			Iterator dIt = dMap.entrySet().iterator();
			while (dIt.hasNext()) {
				Entry entry = (Entry) dIt.next();
				String dPath = (String) entry.getKey();
				dPath = this.getFsPath(dPath, true);

				String lDPath = this.getLocalPath()
						+ System.getProperty("file.separator") + dPath;

				File dFile = new File(lDPath);
				if (!dFile.exists() || !dFile.isDirectory()) {
					if (!dFile.mkdirs()) {
						return false;
					}
				}
			}
		}

		boolean dRs = false;
		for (int i = 0; i < 3; i++) {
			fsMap = this.getServerFsMap();
			dRs = this.downloadServerFs(fsMap);
			this.localFsMap = null;
			this.serverFsMap = null;

			if (dRs) {
				return true;
			}
		}

		return dRs;
	}

	protected boolean downloadServerFs(Map fsMap) {
		if (null == fsMap) {
			return true;
		}

		// 下载文件
		Map<String, Map> lFsMap = this.getLocalFsMap();
		Map<String, String> lFilesMap = new HashMap<String, String>();
		if (!Vaild.isEmpty(lFsMap) && lFsMap.containsKey("f")) {
			lFilesMap = lFsMap.get("f");
		}

		Map paramMap = new HashMap<String, String>();
		if (fsMap.containsKey("f")) {
			Map fMap = (Map) fsMap.get("f");
			Iterator fIt = fMap.entrySet().iterator();
			while (fIt.hasNext()) {
				Entry entry = (Entry) fIt.next();
				String fPath = (String) entry.getKey();

				String localFsPath = this.getLocalPath()
						+ System.getProperty("file.separator")
						+ this.getFsPath(fPath, true);

				// 检验KEY
				if (lFilesMap.containsKey(localFsPath)
						&& fMap.get(fPath).equals(lFilesMap.get(localFsPath))) {
					continue;
				}

				paramMap.clear();
				paramMap.put("action", "download");
				paramMap.put("path", fPath);

				if (localFsPath != null) {
					for (int i = 0; i < 3; i++) {
						Integer dRs = AcDownLoad.post(this.getServerUrl(),
								paramMap, localFsPath, true);

						try {
							if (dRs > 0) {
								if (fMap.get(fPath).equals(
										AcFileMd5.getCheckCode(localFsPath))) {
									break;
								} else {
									AcFile.deleteFile(localFsPath);
									continue;
								}
							} else {
								return false;
							}
						} catch (Exception e) {
							return false;
						}
					}
				}
			}
		}

		return true;
	}

	protected Map getServerFsMap() {
		if (!Vaild.isEmpty(this.serverFsMap)) {
			return this.serverFsMap;
		}

		Map vMap = new HashMap<String, String>();
		vMap.put("action", "list");
		vMap.put("path", this.getServerPath());

		String xml = AcHttp.post(this.getServerUrl(), vMap);
		SAXReader saxReader = new SAXReader();
		try {
			Document xmlDoc = saxReader.read(new ByteArrayInputStream(xml.getBytes()));

			Map xmlMap = XmlDom4j.getMapsByDoc(xmlDoc);

			this.serverFsMap = new HashMap<String, Map>();

			if (xmlMap.containsKey("files")) {
				Map<String, String> fsMap = new HashMap<String, String>();

				Map<String, Object> fMap = (Map) xmlMap.get("files");

				if (fMap.containsKey("file")) {
					Object objs = fMap.get("file");

					if (objs instanceof List) {
						List<Map> fsList = (List<Map>) objs;
						for (int i = 0; i < fsList.size(); i++) {
							Map<String, String> fsMp = fsList.get(i);
							if (fsMp.containsKey("path")
									&& fsMp.containsKey("key")) {
								fsMap.put(fsMp.get("path"), fsMp.get("key"));
							}
						}
					} else if (objs instanceof Map) {
						Map<String, String> fsMp = (Map<String, String>) objs;
						if (fsMp.containsKey("path") && fsMp.containsKey("key")) {
							fsMap.put(fsMp.get("path"), fsMp.get("key"));
						}
					}
					this.serverFsMap.put("f", fsMap);
				}
			}

			if (xmlMap.containsKey("dirs")) {
				Map<String, String> dirMap = new HashMap<String, String>();

				Map<String, Object> dMap = (Map) xmlMap.get("dirs");

				if (dMap.containsKey("file")) {
					Object objs = dMap.get("file");

					if (objs instanceof List) {
						List<Map> dirList = (List<Map>) objs;
						for (int i = 0; i < dirList.size(); i++) {
							Map<String, String> fsMp = dirList.get(i);
							if (fsMp.containsKey("path")) {
								dirMap.put(fsMp.get("path"), fsMp.get("path"));
							}
						}
					} else if (objs instanceof Map) {
						Map<String, String> fsMp = (Map<String, String>) objs;
						if (fsMp.containsKey("path")) {
							dirMap.put(fsMp.get("path"), fsMp.get("path"));
						}
					}

					this.serverFsMap.put("d", dirMap);
				}
			}

		} catch (Exception e) {
			// TODO: handle exception
			this.serverFsMap = null;
		}

		return this.serverFsMap;
	}

	protected Map getFsMap(Map<String, Map> fsMap, boolean isServer) {
		if (null == fsMap) {
			return null;
		}

		Map<String, Map> rsMap = new HashMap<String, Map>();

		if (fsMap.containsKey("d")) {
			Map dMap = new HashMap<String, String>();

			Iterator dIt = fsMap.get("d").entrySet().iterator();
			while (dIt.hasNext()) {
				Entry entry = (Entry) dIt.next();
				String dPath = (String) entry.getKey();
				dMap.put(this.getFsPath(dPath, isServer), fsMap.get("d").get(
						dPath));
			}

			rsMap.put("d", dMap);
		}

		if (fsMap.containsKey("f")) {
			Map fMap = new HashMap<String, String>();

			Iterator fIt = fsMap.get("f").entrySet().iterator();
			while (fIt.hasNext()) {
				Entry entry = (Entry) fIt.next();
				String fPath = (String) entry.getKey();
				fMap.put(this.getFsPath(fPath, isServer), fsMap.get("f").get(
						fPath));
			}

			rsMap.put("f", fMap);
		}

		return rsMap;
	}

	protected String getFsPath(String path, boolean isServer) {
		String rs = path;

		if (isServer) {
			if (rs.startsWith(this.getServerPath())) {
				rs = rs.substring(this.getServerPath().length());
				if (rs.startsWith("\\") || rs.startsWith("/")) {
					rs = rs.substring(1);
				}
			}
		} else {
			if (rs.startsWith(this.getLocalPath())) {
				rs = rs.substring(this.getLocalPath().length());
				if (rs.startsWith("\\") || rs.startsWith("/")) {
					rs = rs.substring(1);
				}
			}
		}

		rs = rs.replace("\\", System.getProperty("file.separator"));
		rs = rs.replace("/", System.getProperty("file.separator"));

		return rs;
	}
}
